package com.xuzimian.globaldemo.spring.springmvc.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 *
 * @author xzm
 * <p>
 * 注意：继承WebMvcConfigurer 接口添加 @EnableWebMvc 注解,如果为实现全部接口方法，会出现 AbstractMethodError错误，既调用了抽象方法。
 *
 * 而继承WebMvcConfigurerAdapter抽象类 添加@EnableWebMvc注解，因为WebMvcConfigurerAdapter 继承WebMvcConfigurer
 * 接口且实现了所有的抽象方法。
 */
@EnableWebMvc
@Configuration
@ComponentScan("com.xuzimian.globaldemo.spring.springmvc.controller")
public class WebConfig implements WebMvcConfigurer  {

    @Autowired
    private StaticPagePathFinder staticPagePathFinder;

    /**
     * 注册拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

    }

    /**
     * 配置HttP消息转换器.
     * 在请求进入Controller方法前，会根据@RequestBody注解选择对应的HttpMessageConverter实现类
     * 来将请求参数解析到param变量中.同理当执行Controller方法后，由于返回值标识了@ResponseBody
     * 将使用StringHttpMessageConverter的write()方法，将结果作为String值写入响应报文，
     * 当然，此时canWrite()方法返回true
     * @param converters
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    }

    /**
     * 配置内容协商机制（一个请求路径返回多种数据格式 如 xml,json,html,excel）
     * ContentNegotationStrategy通过三种方式来识别用户想要返回什么样的数据
     *
     * 通过请求URL后缀：http://myserver/myapp/accounts/list.html 返回html格式
     * 通过请求的参数：http://myserver/myapp/accounts/list?format=xls 该设置默认不开启，默认key是format。
     * 通过HTTP Header的Accept：Accept:application/xml 优先级由上至下
     * @param configurer
     */
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(false)
                .favorParameter(true)
                .parameterName("mediaType")
              //  .defaultContentType(MediaType.APPLICATION_JSON)
                .mediaType("xml", MediaType.APPLICATION_XML)
                .mediaType("html", MediaType.TEXT_HTML)
                .mediaType("json", MediaType.APPLICATION_JSON);
    }

    /**
     * 配置路径匹配规则，如：配置我们的应用程序不使用“.*”的后缀模式匹配，
     * 并且在解析参数时不忽略点之后的值。
     * @param configurer
     */
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {

    }

    /**
     * 注册自定义格式化器和转换器
     * @param registry
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
    }

    /**
     * 配置全局验证器：局部验证器使用@Valid 和 Validated
     * @return
     */
    @Override
    public Validator getValidator() {
        return null;
    }

    /**
     * DefaultServletHttpRequestHandler，这个Handler也是用来处理静态文件的，它会尝试映射/*。
     * 当DispatcherServelt映射/时（/ 和/* 是有区别的），并且没有找到合适的Handler来处理请求时，
     * 就会交给DefaultServletHttpRequestHandler 来处理。
     * DispatcherServlet破坏了Servlet的一个特性（根目录下的文件可以直接访问）,DefaultServletHttpRequestHandler是帮助回归这个特性的。
     * @param configurer
     */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    /**
     * 配置静态资源路径，并使用响应头配置浏览器缓存静态资源。
     * 下面方法中的配置：如果请求以"/resource"开头，则会使用相对路径查找并提供
     * web应用程序根目录下的"/public" 或 类路径下 "/static"的静态资源
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resource/**")
                .addResourceLocations("/public","classpath:/static/")
                .setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic());
    }

    /**
     * 启用全局跨域请求支持.
     * WebMvcConfigurerAdapter的配置最终将会转换为RequestMappingHandlerMapping，
     * 而这个Handler最终是被DispatcherServlet调用，也就是说，配置将会在Servlet中被调用。
     * <p>
     * 拦截器实现跨域方式：
     * httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
     * httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
     * httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
     * <p>
     * 注意：使用此方法配置之后再使用自定义拦截器（interceptor）时跨域相关配置就会失效。
     * 原因是请求经过的先后顺序问题，当请求到来时会先进入拦截器中，而不是进入Mapping映射中，
     * 所以返回的头信息中并没有配置的跨域信息。浏览器就会报跨域异常。
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api")
                .allowedOrigins("http://domain2.com")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                .maxAge(3600);
    }

    /**
     * 添加视图控制器：以下代码它可以方便的实现一个请求直接映射成视图，而无需书写controller。
     * 配置方式：registry.addViewController("请求路径").setViewName("请求页面文件路径")
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //以下代码仅供参考
        try{
            for(StaticPagePathFinder.PagePaths pagePaths :staticPagePathFinder.findPath()){
                String urlPath = pagePaths.getUrlPath();
                System.out.println(pagePaths.getUrlPath());
                System.out.println(pagePaths.getFilePath());
                registry.addViewController(urlPath).setViewName("pages/"+pagePaths.getFilePath());
                if(!urlPath.isEmpty()){
                    registry.addViewController(urlPath).setViewName("pages/"+pagePaths.getFilePath());
                }
            }
        }catch(IOException e){
            throw new RuntimeException("Unable to locate static pages:"+e.getMessage(),e);
        }
    }

    /**
     * 配置视图解析器
     * @param registry
     */
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        registry.viewResolver(resolver);
        // registry.jsp();
    }

    /**
     * 视图解析器配置
     * @return
     */
    //@Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }


    /**
     * 上传文件配置设置
     * @return
     */
    //@Bean
    public MultipartResolver multipartResolver() throws IOException {
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
        multipartResolver.setUploadTempDir(new FileSystemResource("/tmp/hc/uploads"));
        multipartResolver.setMaxUploadSize(2097152L);
        multipartResolver.setMaxInMemorySize(0);
        return multipartResolver;
    }


    /**
     * 全局异常处理器
     * @param exceptionResolvers
     */
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    }


    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {

    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {

    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {

    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {

    }

    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {

    }

    @Override
    public MessageCodesResolver getMessageCodesResolver() {
        return null;
    }
}
